package com.course.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.course.common.enums.Constant;
import com.course.common.util.JwtUtil;
import io.jsonwebtoken.Claims;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.Objects;

/**
 * @PROJECT_NAME: course
 * @DESCRIPTION: 登录用户具体业务实现类-网关路由过滤器
 * GlobalFilter接口和GatewayFilter有一样的接口定义，只不过，GlobalFilter会作用于所有路由，
 * 我们可以用它来实现很多统一化处理的业务需求，比如权限认证，IP访问限制等等；而GatewayFilter作用于某一个特定的路由，
 * GlobalFilter全局过滤器是一系列特殊的过滤器，会根据条件应用到所有路由中。GatewayFilter网关过滤器是更细粒度的过滤器，
 * 作用于指定的路由中。
 * @Author: 涂玄武
 * @DATE: 2021/4/28 10:36
 */
@Component
public class LoginAdminGatewayFilter implements GatewayFilter, Ordered {

    private static final Logger logger = LoggerFactory.getLogger(LoginAdminGatewayFilter.class);

    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 网关过滤配置
     *
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取Request、Response对象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();


        //获取请求的URI
        String path = request.getURI().getPath();

        //请求地址中不包含/admin/的，不是控台请求，不需要拦截
        if (!path.contains("/admin/")) {
            return chain.filter(exchange);
        }
        if (path.contains("/system/admin/user/login")
                || path.contains("/system/admin/user/logout")
                || path.contains("/system/admin/kaptcha")) {
            logger.info("不需要控台登录验证：{}", path);
            return chain.filter(exchange);
        }

        //获取header的token参数
        String token = request.getHeaders().getFirst("token");
        logger.info("登录拦截开始，token:{}", token);
        if (StringUtils.isEmpty(token)) {
            //如果header中没有，则从请求参数中获取
            token = request.getQueryParams().getFirst("token");
        }

        //从Cookie中获取令牌数据
//        HttpCookie first = request.getCookies().getFirst("Authorization");
//        if (Objects.nonNull(first)){
//            token = first.getValue();
//        }

        //如果为空，则输出错误代码405
        if (StringUtils.isEmpty(token)) {
            logger.info("token为空，请求被拦截");
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }

        //获取令牌数据
        Object jwt = redisTemplate.opsForValue().get(Constant.REDIS_LOGIN_PREFIX + token);
        if (Objects.isNull(jwt)) {
            logger.warn("token无效，请求被拦截");
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        } else {
            Claims claims = JwtUtil.parseJWT((String) jwt);

//            //将令牌数据添加到请求头文件中
//            request.mutate().header("Authorization",claims.getSubject());

            logger.info("已登录：{}", claims);

            //增加权限校验，gateway里没有LoginUserDto，所以全部用JSON操作
            logger.info("接口权限校验，请求地址：{}", path);
            boolean exist = false;
            JSONObject loginUserDto = JSON.parseObject(String.valueOf(claims.getSubject()));
            JSONArray requests = loginUserDto.getJSONArray("requests");
            //遍历所有【权限请求】，判断当前请求的地址是否在【权限请求】中
            for (int i = 0; i < requests.size(); i++) {
                String requestPath = (String) requests.get(i);
                if (path.contains(requestPath)) {
                    exist = true;
                    break;
                }
            }
            if (exist) {
                logger.info("权限校验通过");
            } else {
                logger.warn("权限校验未通过");
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                return response.setComplete();
            }

            return chain.filter(exchange);
        }
    }

    @Override
    public int getOrder() {
        return 1;
    }

}
